﻿using System;
using System.Data.Services.Client;
using System.Linq;
using nRoute.Components;
using nRoute.Samples.SiteMapClient.SiteMapService;
using nRoute.SiteMaps;

namespace nRoute.Samples.SiteMapClient.Components
{
	public class RemoteSiteMapProvider : ISiteMapProvider
	{
		private const string SERVICE_PATH = "Services/SiteMapDataService.svc/";
		private const string APPLICATION_MUST_SPECIFY = "ApplicationName name must be specified.";

		private readonly RelayObservable<SiteMap> _siteMapRelay;
		private readonly ApplicationSiteMapContext _siteMapContext;
		private DataServiceCollection<ApplicationSiteMap> _siteMapCollection;

		public RemoteSiteMapProvider()
		{
			_siteMapRelay = new RelayObservable<SiteMap>();
			_siteMapContext = new ApplicationSiteMapContext(new Uri(SERVICE_PATH, UriKind.Relative));
		}

		public string ApplicationName { get; set; }

#region ISiteMapProvider

		public IObservable<SiteMap> ResolveSiteMap()
		{
			if (string.IsNullOrWhiteSpace("ApplicaitonName")) throw new InvalidOperationException(APPLICATION_MUST_SPECIFY);
						
			_siteMapCollection = new DataServiceCollection<ApplicationSiteMap>();
			_siteMapCollection.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(SiteMapCollection_LoadCompleted);
			
			try
			{
				var _query = _siteMapContext.ApplicationSiteMapSet
					.Expand("RootSiteMapNode")
					.Expand("RootSiteMapNode/SiteMapArea")
					.Expand("RootSiteMapNode/ChildNodes")
					.Expand("RootSiteMapNode/ChildNodes/ChildNodes")
					.Expand("RootSiteMapNode/ChildNodes/SiteMapArea")
					.Expand("SiteMapAreas")
					.Expand("SiteMapAreas/AreaDependencies")
					.Where((a) => a.ApplicationName == this.ApplicationName);
				_siteMapCollection.LoadAsync(_query);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			return _siteMapRelay;
		}

#endregion

#region Handlers

		private void SiteMapCollection_LoadCompleted(object sender, LoadCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				_siteMapRelay.OnError(e.Error);
			}
			else
			{
				var _applicationSiteMap = _siteMapCollection.First();		// will throw an error if not found				
				var _siteMap = new SiteMap();

				if (_applicationSiteMap.SiteMapAreas != null && _applicationSiteMap.SiteMapAreas.Count > 0)
				{ 
					_siteMap.Areas = new AreasCollection();
					foreach (var _area in _applicationSiteMap.SiteMapAreas)
					{
						_siteMap.Areas.Add(ResolveApplicationSiteMapArea(_area));
					}
				}

				_siteMap.RootNode = ResolveApplicationSiteMapNode(_applicationSiteMap.RootSiteMapNode);
				
				_siteMapRelay.OnNext(_siteMap);
				_siteMapRelay.OnCompleted();
			}
		}

#endregion

#region Helpers

		private NavigationNode ResolveApplicationSiteMapNode(ApplicationSiteMapNode node)
		{
			if (node == null) return null;
			var _navigationNode = new NavigationNode()
			{
				Key = node.Key,
				Title = node.Title,
				Description = node.Description,
				Url = node.Url,
				HandlerName = node.HandlerName,
				IsListed = node.IsListed				
			};

			if (node.SiteMapArea != null)
			{
				_navigationNode.SiteArea = node.SiteMapArea.Key;
			}

			if (node.ChildNodes != null && node.ChildNodes.Count > 0)
			{
				_navigationNode.ChildNodes = new NodesCollection();
				foreach (var _child in node.ChildNodes)
				{
					_navigationNode.ChildNodes.Add(ResolveApplicationSiteMapNode(_child));
				}
			}

			return _navigationNode;
		}

		private SiteArea ResolveApplicationSiteMapArea(ApplicationSiteMapArea area)
		{
			if (area == null) return null;
			var _area = new SiteArea()
			{
				Key = area.Key,
				RemoteUrl = area.RemoteUrl,
				InitializeOnLoad = area.InitializeOnLoad
			};
			
			if (area.AreaDependencies != null && area.AreaDependencies.Count > 0)
			{
				_area.AreaDependencies = new AreaInfosCollection();
				foreach (var _areaDependency in area.AreaDependencies)
				{
					_area.AreaDependencies.Add(new SiteAreaInfo()
					{
						Key = _areaDependency.Key
					});
				}
			}

			return _area;
		}

#endregion

	}
}